package com.dongway.netcondition;

import java.io.InputStream;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.sql.DataSource;

import org.apache.commons.io.Charsets;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.sitemesh.config.ConfigurableSiteMeshFilter;
import org.slf4j.Logger;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.boot.context.web.SpringBootServletInitializer;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.dongway.core.converter.MappingJsonpHttpMessageConverter;
import com.dongway.core.utils.ApplicationContextUtils;
import com.dongway.netcondition.security.SimpleRealm;

@SpringBootApplication
@ComponentScan(basePackages = { "com.dongway" }, lazyInit = true)
public class Application extends SpringBootServletInitializer {

    private static Logger logger = org.slf4j.LoggerFactory.getLogger(Application.class);


    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }


    @Bean
    public DataSource dataSource() {
        try {
            InputStream is =
                    Application.class.getClassLoader().getResourceAsStream("db.properties");
            Properties prop = new Properties();
            prop.load(is);
            IOUtils.closeQuietly(is);
            DruidDataSource ds = new DruidDataSource();
            Map<String, String> map = new HashMap<String, String>();
            map.put(DruidDataSourceFactory.PROP_URL, prop.getProperty("database.url"));
            map.put(DruidDataSourceFactory.PROP_USERNAME, prop.getProperty("database.username"));
            map.put(DruidDataSourceFactory.PROP_PASSWORD, prop.getProperty("database.password"));
            Set<Object> keys = prop.keySet();
            for (Object key : keys) {
                String _key = key.toString();
                if (_key.startsWith("pool")) {
                    String skey = _key.substring(5);
                    map.put(skey, prop.getProperty(_key));
                }
            }
            DruidDataSourceFactory.config(ds, map);
            return ds;
        } catch (Exception e) {
            logger.warn(e.getMessage(), e);
        }
        return null;
    }


    @Bean
    public ApplicationContextAware getContextProvider() {
        return new ApplicationContextUtils();
    }


    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    @Configuration
    public static class ShiroConfig extends WebMvcConfigurerAdapter {

        @Bean
        public Realm realm() {
            SimpleRealm realm = new SimpleRealm();
            realm.setAuthorizationCachingEnabled(true);
            realm.setCachingEnabled(true);
            return realm;
        }


        @Bean
        public SecurityManager securityManager() {
            DefaultWebSecurityManager mgr = new DefaultWebSecurityManager();
            mgr.setRealm(realm());
            mgr.setCacheManager(new MemoryConstrainedCacheManager());
            return mgr;
        }


        @Bean(name = "shiroFilter", autowire = Autowire.BY_TYPE)
        public ShiroFilterFactoryBean shiroFilterFactory() {
            ShiroFilterFactoryBean bean = new org.apache.shiro.spring.web.ShiroFilterFactoryBean();
            bean.setSecurityManager(securityManager());
            bean.setLoginUrl("/login.htm");
            bean.setSuccessUrl("/manager.htm");
            bean.setUnauthorizedUrl("/unauthorized.htm");
            Map<String, String> filterChainDefinitionMap = new HashMap<String, String>();
            // filterChainDefinitionMap.put("/register.htm", "anon");
            filterChainDefinitionMap.put("/configure.htm", "authc");
            filterChainDefinitionMap.put("/**", "anon");
            bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            Map<String, Filter> filters = new HashMap<String, Filter>();
            bean.setFilters(filters);
            return bean;
        }


        @Bean
        public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
            return new org.apache.shiro.spring.LifecycleBeanPostProcessor();
        }


        @Bean
        public DefaultAdvisorAutoProxyCreator DefaultAdvisorAutoProxyCreator() {
            DefaultAdvisorAutoProxyCreator proxy = new DefaultAdvisorAutoProxyCreator();
            proxy.setExposeProxy(true);
            proxy.setProxyTargetClass(true);
            return proxy;
        }


        @Bean
        public AuthorizationAttributeSourceAdvisor AuthorizationAttributeSourceAdvisor(
                SecurityManager securityManager) {
            AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
            advisor.setSecurityManager(securityManager);
            return advisor;

        }
    }

    @Configuration
    @AutoConfigureAfter(ShiroConfig.class)
    public static class MvcConfig extends WebMvcConfigurerAdapter {

        // @Autowired
        // private ShiroFilterFactoryBean shiroFilterFactoryBean;

        @Bean
        public FilterRegistrationBean characterEncodingFilter() {
            CharacterEncodingFilter encoding = new CharacterEncodingFilter();
            encoding.setEncoding("utf-8");
            encoding.setForceEncoding(true);
            FilterRegistrationBean registration = new FilterRegistrationBean(encoding);
            registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
            registration.addUrlPatterns("/*");
            return registration;
        }


        // @Bean(autowire = Autowire.BY_TYPE)
        // public FilterRegistrationBean shiroFilter() throws Exception {
        // Filter filter = (Filter) shiroFilterFactoryBean.getObject();
        // FilterRegistrationBean registration = new FilterRegistrationBean(filter);
        // registration.addUrlPatterns("/*");
        // return registration;
        // }

        @Bean
        public FilterRegistrationBean sitemeshFilter() {
            ConfigurableSiteMeshFilter sitemesh = new ConfigurableSiteMeshFilter();
            FilterRegistrationBean registration = new FilterRegistrationBean(sitemesh);
            registration.addInitParameter("configFile", "/WEB-INF/sitemesh.xml");
            registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
            registration.addUrlPatterns("*.htm");
            return registration;
        }


        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/resources/**").addResourceLocations("/resources/",
                "classpath:/META-INF/");
        }


        @Override
        public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
            super.configureContentNegotiation(configurer);
            configurer.defaultContentType(MediaType.TEXT_HTML);
            configurer.mediaType("html", MediaType.TEXT_HTML);
            configurer.mediaType("json", MediaType.APPLICATION_JSON);
        }


        @Override
        public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
            super.configureMessageConverters(converters);
            HttpMessageConverter<String> string = new StringHttpMessageConverter(Charsets.UTF_8);
            MappingJsonpHttpMessageConverter json = new MappingJsonpHttpMessageConverter();
            json.setFeatures(SerializerFeature.QuoteFieldNames,
                SerializerFeature.WriteDateUseDateFormat,
                SerializerFeature.DisableCircularReferenceDetect);
            ByteArrayHttpMessageConverter byteArray = new ByteArrayHttpMessageConverter();
            converters.add(string);
            converters.add(json);
            converters.add(byteArray);
        }


        @Bean
        public ViewResolver getViewResolver() {
            InternalResourceViewResolver vr = new InternalResourceViewResolver();
            vr.setPrefix("/WEB-INF/views/");
            vr.setSuffix(".jsp");
            return vr;
        }
    }
}
